#!/usr/bin/python3
# -*- coding: utf-8 -*-

import ssl
import time
from copy import deepcopy
from time import perf_counter
from typing import Any, Dict, List, Set, Tuple, Union

import allure

ssl._create_default_https_context = ssl._create_unverified_context
from qingcloud.iaas.connection import APIConnection

from utils.cache_util import cache_manager
from utils.log import log
from utils.str_util import to_list
from utils.valid_util import case_assert


class MixinUtil:

    wait_leased = cache_manager.get('wait_leased')

    @staticmethod
    def send_req(action, body):
        client = APIConnection(
            qy_access_key_id=cache_manager.get('qy_access_key_id'),
            qy_secret_access_key=cache_manager.get('qy_secret_access_key'),
            zone=cache_manager.get('region'),
            host=cache_manager.get('api_host'),
            port=cache_manager.get('port'),
            protocol=cache_manager.get('protocol'),
            debug=False,
        )
        log.debug(f"请求数据:{body}")
        rep = client.send_request(action, body, url="/iaas/", verb="GET")
        log.debug(f"响应数据:{rep}")
        return rep

    @classmethod
    def __wait_jobs(
            cls, job_ids: List[str], timeout: int = 600, interval: int = 3
    ) -> [str, bool]:
        """
        等待任务完成(成功或失败)直到超时
        :param job_ids: job_id
        :param timeout: 超时时间,默认600s
        :param interval:  轮询间隔
        :return:True or False
        """
        log.info(f"=== 查询job状态: {job_ids} ===")

        def describe_job(job_ids):
            if isinstance(job_ids, str):
                job_ids = [job_ids]
            res = cls.send_req(
                action="DescribeJobs", body={"action": "DescribeJobs", "jobs": job_ids}
            )
            case_assert.assert_is_not_none(res)
            case_assert.assert_code(res)
            if not res or not res.get("job_set"):
                return None
            return res["job_set"][0]

        deadline = time.time() + timeout
        start_time = perf_counter()
        while time.time() <= deadline:
            res = describe_job(job_ids)
            if not res:
                continue
            job_status = res["status"]
            if job_status in ["successful", "failed"]:
                elapsed_time = perf_counter() - start_time
                log.info(f"job: {job_ids} 执行: {job_status} 耗时: {elapsed_time:.2f} s")
                return job_status
            else:
                time.sleep(interval)
        log.error(f"### job: {job_ids} 执行超时:{timeout} s,请注意人工确认原因!!! ###")
        return False

    @staticmethod
    def extract_action(response: Dict[str, Any]) -> [str, None]:
        """
        提取job action name
        :param response:
        :return:
        """
        if not response:
            return None
        ret_code = response.get("ret_code")
        if ret_code == 0:
            action_name: str = response.get("action").replace("Response", "")
            return action_name
        else:
            return None

    @classmethod
    def extract_job(cls, response: Dict[str, Any]) -> [str, None]:
        """
        提取job id
        :param response:
        :return:
        """
        if not response:
            return None
        log.info(response)
        action_name: str = cls.extract_action(response)
        job_id: str = response.get("job_id")
        job_ids: list = response.get("job_ids")
        if job_id:
            log.info(f"=== 执行:{action_name},提取的job_id为: {job_id} ===")
            return job_id
        if job_ids:
            log.info(f"=== 执行:{action_name},提取的job_ids为: {job_ids} ===")
            return job_ids
        return None

    @allure.step("等待job状态")
    def wait_jobs(
            self,
            response: Dict[str, Any],
            timeout: int = 600,
            interval: int = 3,
            check_status: bool = False,
    ) -> [str, None]:
        """
        自动从response中提取job_id 并执行等待
        :param response: 响应
        :param timeout: 超时时间
        :param interval: 轮询间隔
        :param check_status: 是否检查job的执行结果
        :return:
        """
        job_id = self.extract_job(response)
        if not job_id:
            return None
        job_ids = [job_id] if isinstance(job_id, list) else job_id
        job_status = self.__wait_jobs(job_ids, timeout, interval)
        if check_status:
            case_assert.assert_eq(
                job_status, "successful", f"job:{job_id} 执行:{job_status}"
            )
        return job_status

    def get_lease_info(self, resource, user=None):
        http_data = {
            "action": "GetLeaseInfo",
            "resource": resource,
            "user": user,
        }
        resp = self.send_req(action=http_data["action"], body=http_data)
        return resp

    @allure.step("等待资源租赁信息")
    def wait_lease_status(self,
                          resources: List[str],
                          status: Union[List[str], Tuple[str], Set[str]],
                          time_out: int = 600,
                          ) -> None:
        """
        等待资源租赁状态就绪
        @param resources: 等待的资源
        @param status: 状态
        @param time_out: 设置超时时间
        """
        if not isinstance(resources, list):
            resources = [resources]
        if self.wait_leased:
            log.info(f"正在等待资源:{resources} 的租赁信息中...")
            no_ready_res = deepcopy(resources)
            cnt = 0
            sleep_interval = 10
            while cnt < time_out / sleep_interval:
                for resource_id in no_ready_res:
                    rep = self.get_lease_info(resource_id)
                    case_assert.assert_code(rep)
                    log.info(rep)
                    lease_info = rep['lease_info']
                    if lease_info['status'] in to_list(status):
                        no_ready_res.remove(resource_id)
                if len(no_ready_res) == 0:
                    break
                cnt += 1
                time.sleep(sleep_interval)
            case_assert.assert_eq(0,
                                  len(no_ready_res),
                                  "等待资源 [%s] 的租赁信息为 [%s] 状态时在billing server超时" % (no_ready_res, status))
        else:
            log.info(f"当前环境:{cache_manager.get('env')} 跳过租赁信息检查")

    def describe_router_vxnets(self, router):
        """查询vpc下的私有网络"""
        http_data = {
            "action": "DescribeRouterVxnets",
            "router": router,
            "offset": 0,
            "limit": 100,
        }
        return self.send_req(action=http_data["action"], body=http_data)

    def get_router_vxnet_ids(self, router):
        """
        获取router 下绑定的vxnet ids
        :param router_vxnets: describe_router_vxnets 接口的返回值
        :return:
        """
        vxnet_ids = []
        router_vxnets = self.describe_router_vxnets(router)
        for vxnet in router_vxnets['router_vxnet_set']:
            vxnet_id = vxnet.get('vxnet_id')
            vxnet_name = vxnet.get('vxnet_name')
            if vxnet_name != "AppCenter Metadata Service":
                vxnet_ids.append(vxnet_id)
            else:
                continue
        log.info(f"router下绑定的私有网络数量为: {len(vxnet_ids)}，分别为: {vxnet_ids}")
        return vxnet_ids
